package org.tea.mapper.util;

import cn.hutool.core.lang.Pair;
import org.apache.ibatis.builder.annotation.ProviderContext;
import org.apache.ibatis.logging.Log;
import org.apache.ibatis.logging.LogFactory;

import java.util.*;

/**
 * @author lxh
 */
public class BaseUpdateMapperProvider
        extends MapperHelper {

    private final static Log log = LogFactory.getLog(BaseInsertMapperProvider.class);

    public String updateByPrimaryKeySelective(Object param, ProviderContext providerContext) {
        Class<?> mapperType = providerContext.getMapperType();
        Class<?> entityType = parseActualEntityType(mapperType);
        String tableName = parseTableName(entityType);

        Map<Pair<String, Class<?>>, String> field2Column = buildField2Column(entityType);
        StringBuilder updateSqlBuilder = buildCommonUpdateSql(tableName);
        List<String> updateValList = buildUpdateValues(param, field2Column);
        updateSqlBuilder.append("\n")
                .append("<trim suffixOverrides=\",\">")
                .append(String.join("\n", updateValList))
                .append("</trim>")
                .append(buildPkWhereSql(entityType, param))
                .append("</script>");
        log.trace(String.format("updateByPrimaryKey actual sql: %s", updateSqlBuilder));
        return updateSqlBuilder.toString();
    }

    public String updateSelectiveBYPrimaryKeyBatch(Object param, ProviderContext providerContext) {
        Class<?> mapperType = providerContext.getMapperType();
        Class<?> entityType = parseActualEntityType(mapperType);
        String tableName = parseTableName(entityType);
        final Map<Pair<String, Class<?>>, String> field2Column = buildField2Column(entityType);
        Map<String, String> pk2FieldMap = parsePrimaryKey2FieldName(entityType);

        // 整合需要更新的记录行

        StringBuilder updateSqlBuilder = buildCommonUpdateSql(tableName).append("\n");
        updateSqlBuilder.append("<trim suffixOverrides=\",\">");
        ;
        for (Map.Entry<Pair<String, Class<?>>, String> entry : field2Column.entrySet()) {
            Pair<String, Class<?>> pair = entry.getKey();
            updateSqlBuilder
                    .append("<trim prefix=\"").append(entry.getValue()).append("= CASE\" >")
                    .append("<foreach collection=\"param\" item=\"item\">")
                    .append("\tWHEN ").append(buildPkWhereCondSql(pk2FieldMap)).append(" THEN ")
                    .append("<choose>")
                    .append("<when test=\"item.").append(pair.getKey()).append(" != null\">")
                    .append(" ").append("#{").append("item.").append(pair.getKey()).append("}").append("\n")
                    .append("</when>")
                    .append("<otherwise>")
                    .append(" ").append(entry.getValue()).append("\n")
                    .append("</otherwise>")
                    .append("</choose>")
                    .append("</foreach>")
                    .append(" ELSE ").append(entry.getValue())
                    .append(" END,").append("\n")
                    .append("</trim>");
        }
        updateSqlBuilder.append("</trim>");
        updateSqlBuilder.append(buildPkWhereSql(entityType, param));
        updateSqlBuilder.append("</script>");

        log.trace("updateSelectiveBatch actual sql: " + updateSqlBuilder);
        return updateSqlBuilder.toString();
    }

    private static String buildPkWhereCondSql(Map<String, String> pk2FieldMap) {
        StringBuilder sb = new  StringBuilder();
        List<String> list = new ArrayList<>();
        for (Map.Entry<String, String> entry : pk2FieldMap.entrySet()) {
            list.add(entry.getKey() + "=#{item." + entry.getValue() + "} ");
        }
        return String.join(" AND ", list);
    }

    private static StringBuilder buildCommonUpdateSql(String tableName) {
        StringBuilder updateSqlBuilder = new StringBuilder();
        updateSqlBuilder.append("<script>")
                .append("UPDATE ").append(tableName).append(" SET ");
        return updateSqlBuilder;
    }

    private static List<String> buildUpdateValues(Object param, Map<Pair<String, Class<?>>, String> field2Column) {
        List<String> updateValList = new ArrayList<>();
        for (Map.Entry<Pair<String, Class<?>>, String> entry : field2Column.entrySet()) {
            Pair<String, Class<?>> pair = entry.getKey();
            String columnName = entry.getValue();

            String updatedVal = "<if test=\"param." + pair.getKey() + "!=null\">";
            updateValList.add(updatedVal + " " + columnName + "=#{param." + pair.getKey() + "}," + "</if>\n");
        }
        return updateValList;
    }
}
